/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import fs from '@ohos.file.fs';
import {BusinessError} from '@ohos.base'
import util from '@ohos.util';
import TLog from './TLog';
import common from '@ohos.app.ability.common';

const TAG:string = 'FileUtils'
export class FileUtils {
    static readonly SEPARATOR:string = '/'
    base64Str:string = ''
    private static sInstance:FileUtils;

    public static getInstance():FileUtils {
        if (!FileUtils.sInstance) {
            FileUtils.sInstance = new FileUtils();
        }
        return FileUtils.sInstance;
    }

    private constructor() {
    }

    /**
     * 获取path的文件大小
     */
    getFileSize(path:string):number {
        try {
            let stat = fs.statSync(path)
            return stat.size
        } catch (e) {
            console.error("FileUtils - FileUtils getFileSize e " + e)
            return -1
        }
    }

    /**
     * 同步读取路径path的文件
     */
    readFilePic(path:string):ArrayBuffer {
        try {
            let stat = fs.statSync(path)
            let fd = fs.openSync(path, fs.OpenMode.READ_ONLY).fd;
            let length = stat.size
            let buf = new ArrayBuffer(length);
            fs.readSync(fd, buf)
            fs.closeSync(fd)
            return buf
        } catch (e) {
            console.log("FileUtils - readFilePicSync " + e)
            return new ArrayBuffer(0)
        }
    }

    /**
     * 异步读取路径path的文件
     */
    readFilePicAsync(path:string):Promise<ArrayBuffer> {
        return new Promise((resolve, reject) => {
            fs.open(path, fs.OpenMode.READ_ONLY).then((file) => {
                let stat = fs.statSync(path)
                let fd = file.fd;
                let length = stat.size;
                let buf = new ArrayBuffer(length);
                fs.read(fd, buf).then((readLen) => {
                    // 关闭文件
                    fs.closeSync(file);
                    resolve(buf);
                }).catch((err:BusinessError) => {
                    reject(err);
                })
            }).catch((err:BusinessError) => {
                reject(err);
            })
        })
    }

    /**
     * stream式读取
     */
    readStream(path:string):string {
        try {
            let stat = fs.statSync(path)
            let length = stat.size
            let buf = new ArrayBuffer(length);
            let ss = fs.createStreamSync(path, "r+");
            ss.readSync(buf)
            ss.closeSync();
            let u8:Uint8Array = new Uint8Array(buf);
            let array:Array<number> = Array.from(u8)
            return String.fromCharCode(...array)
        } catch (e) {
            console.log("FileUtils - readFilePic " + e)
            return ""
        }
    }

    /**
     * string 转 Uint8Array
     * @param str 输入String
     */
    stringToUint8Array(str:string):Uint8Array {
        let arr:Array<number> = new Array<number>();
        for (let i = 0, j = str.length; i < j; ++i) {
            arr.push(str.charCodeAt(i));
        }
        let tmpUint8Array = new Uint8Array(arr);
        return tmpUint8Array
    }








    uint8ArrayToBuffer(array:Uint8Array):ArrayBuffer {
        return array.buffer.slice(array.byteOffset, array.byteLength + array.byteOffset)
    }


    /**
     * 创建文件夹
     *
     * @param path 文件夹绝对路径，只有是权限范围内的路径，可以生成
     * @param recursive
     */
    public createFolder(path: string, recursive?: boolean) {
        try {
            if (recursive) {
                if (!this.existFolder(path)) {
                    let lastInterval = path.lastIndexOf(FileUtils.SEPARATOR)
                    if (lastInterval == 0) {
                        return
                    }
                    let newPath = path.substring(0, lastInterval)
                    this.createFolder(newPath, true)
                    if (!this.existFolder(path)) {
                        fs.mkdirSync(path)
                    }
                }
            } else {
                if (!this.existFolder(path)) {
                    fs.mkdirSync(path)
                }
            }
        } catch (e) {
            console.log("createFolder err : " + e)
        }

    }



    public writeFileAsync(sandboxPath:string,content:string|ArrayBuffer):Promise<boolean>{
       return new Promise((resolve,reject)=>{
           let folderPath = sandboxPath.substring(0, sandboxPath.lastIndexOf(FileUtils.SEPARATOR));
           let fileName = sandboxPath.substring(sandboxPath.lastIndexOf(FileUtils.SEPARATOR)+1);
           this.createFolder(folderPath,true)
           TLog.debug(TAG,`sandboxPath=${sandboxPath}`)
           let fd = fs.openSync(sandboxPath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE).fd
           fs.truncateSync(fd)
           fs.write(fd, content).then((num: number) => {
                   fs.close(fd).then(()=>{
                        resolve(true)
                   })
           }).catch((e: BusinessError) => {
                  reject(e)
               })
       })
    }

    public backupWriteFileMode(sandboxPath:string,content:string|ArrayBuffer,mode?:number):Promise<boolean>{
        return new Promise((resolve,reject)=>{
            let bakFilePath = sandboxPath+'.bak'
            fs.copyFile(sandboxPath,bakFilePath).then(()=>{
                // 临时文件.bak拷贝完成
                this.writeFileAsyncMode(bakFilePath,content,mode).then((writeOk)=>{
                    if(writeOk){
                        // 临时文件写入成功,接下来需要把临时文件重命名为原文件
                        fs.rename(bakFilePath,sandboxPath).then(()=>{
                            resolve(true)
                        }).catch((err:BusinessError)=>{
                            reject(err)
                        })
                    }else{
                        // 临时文件写入失败
                        resolve(false)
                    }
                })
            }).catch((err:BusinessError)=>{
                reject(err)
            })
        })
    }

    /**
     * 异步存文件
     * @param sandboxPath 沙盒路径
     * @param content 文件内容
     * @param mode 打开方式 0:append 1:清空后存入 默认不传走append
     * @returns
     */
    public writeFileAsyncMode(sandboxPath:string,content:string|ArrayBuffer,mode?:number):Promise<boolean>{
       return new Promise((resolve,reject)=>{
           let folderPath = sandboxPath.substring(0, sandboxPath.lastIndexOf(FileUtils.SEPARATOR));
           let fileName = sandboxPath.substring(sandboxPath.lastIndexOf(FileUtils.SEPARATOR)+1);
           this.createFolder(folderPath,true)
           TLog.debug(TAG,`sandboxPath=${sandboxPath}`)
           let fd = fs.openSync(sandboxPath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE).fd
           if(mode == undefined || mode == 0){
               let stat = fs.statSync(sandboxPath)
               fs.write(fd, content,{offset:stat.size}).then((num: number) => {
                   fs.close(fd).then(()=>{
                       resolve(true)
                   })
               }).catch((e: BusinessError) => {
                   reject(e)
               })
           }else{
               fs.truncateSync(fd)
               fs.write(fd, content).then((num: number) => {
                   fs.close(fd).then(()=>{
                       resolve(true)
                   })
               }).catch((e: BusinessError) => {
                   reject(e)
               })
           }
       })
    }





    /**
     * 判断文件夹是否存在
     *
     * @param path 文件夹绝对路径
     */
    private existFolder(path: string): boolean {
        try {
            if (fs.accessSync(path)) {
                let stat = fs.statSync(path)
                return stat.isDirectory()
            } else {
                return false
            }
        }
        catch (error) {
            let err: BusinessError = error as BusinessError;
            console.error("accessSync failed with error message: " + err.message + ", error code: " + err.code);
            return false
        }
    }


    public  saveBase64Picture2File(base64Picture:string, dstPath:string):Promise<boolean> {
       return new Promise((resolve,reject)=>{
           // data:image/png;base64截取了这一段
           let base64Data = base64Picture.split(',')[1];
           let base64 = new util.Base64Helper();
           let data = base64.decodeSync(base64Data);
           this.writeFileAsync(dstPath, data.buffer).then((writeOk)=>{
               if(writeOk){
                   resolve(true)
               }
           })
       })
    }



    public formatyyyyMMdd(timestamp: number): string {
        const date = new Date(timestamp);
        // 获取年月日
        const year = date.getFullYear();
        const month = String(date.getMonth() + 1).padStart(2, '0'); // 月份从0开始，需要+1
        const day = String(date.getDate()).padStart(2, '0');
        // 拼接成 yyyyMMdd 格式
        return `${year}${month}${day}`;
    }
    public formatyyyyMMddHHmmss(timestamp: number): string {
        const date = new Date(timestamp);
        // 获取年月日时分秒
        const year = date.getFullYear();
        const month = String(date.getMonth() + 1).padStart(2, '0'); // 月份从0开始，需要+1
        const day = String(date.getDate()).padStart(2, '0');
        const hours = String(date.getHours()).padStart(2, '0');
        const minutes = String(date.getMinutes()).padStart(2, '0');
        const seconds = String(date.getSeconds()).padStart(2, '0');

        // 拼接成字符串
        return `${year}${month}${day}${hours}${minutes}${seconds}`;
    }

    public formatyyyyMMddHHmmssCN(timestamp: number): string {
        const date = new Date(timestamp);
        // 获取年月日时分秒
        const year = date.getFullYear();
        const month = String(date.getMonth() + 1).padStart(2, '0'); // 月份从0开始，需要+1
        const day = String(date.getDate()).padStart(2, '0');
        const hours = String(date.getHours()).padStart(2, '0');
        const minutes = String(date.getMinutes()).padStart(2, '0');
        const seconds = String(date.getSeconds()).padStart(2, '0');

        // 拼接成字符串
        return `${year}年${month}月${day}日 ${hours}时${minutes}分${seconds}秒`;
    }




    /**
     * 删除当前文件夹下所有文件
     * @param folderPath 文件夹路径
     * @param recursion 是否包含子文件夹
     * @returns
     */
    public clearFolderAsync(folderPath:string,recursion?:boolean):Promise<boolean>{
        return new Promise((resolve,reject)=>{
            if(recursion){
                // 递归删除目录及其内容的函数
                function deleteFolderRecursive(path) {
                    return fs.listFile(path).then((entries) => {
                        let promises = entries.map((entry) => {
                            const fullPath = `${path}/${entry}`;
                            return fs.stat(fullPath).then((stat) => {
                                if (stat.isDirectory()) {
                                    // 如果是文件夹，递归删除
                                    return deleteFolderRecursive(fullPath);
                                } else {
                                    // 如果是文件，直接删除
                                    return fs.unlink(fullPath);
                                }
                            });
                        });

                        // 等待所有子文件和子文件夹处理完毕后，保留当前文件夹
                        return Promise.all(promises).then(() => {
                            resolve(true)
                        });
                    });
                }

                deleteFolderRecursive(folderPath)


            }else{
                // 删除文件夹下的文件
                fs.listFile(folderPath).then((filenames:string[])=>{
                    let filepath = folderPath+'/'+filenames;
                    let stat = fs.statSync(filepath)
                    if(!stat.isDirectory()){
                        fs.unlinkSync(filepath)
                    }
                    resolve(true)
                })
            }
        })
    }

    public deleteFileAsync(path: string): Promise<void> {
        return new Promise((resolve,reject)=>{
            fs.access(path).then(fileExist =>{
                if (fileExist) {
                    fs.unlink(path).then(()=>{
                        resolve();
                    }).catch( (err:BusinessError)=>{
                        reject(err)
                    })
                }else{
                    let error:Error ={
                        message:'删除文件出错,文件不存在',
                        name:'文件不存在',
                    }
                    reject(error)
                }
            }).catch((err:BusinessError)=>{
                reject(err);
            })
        })
    }

}